﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace iNet.Media.Imaging.Exif
{
	/// <summary>
	/// TIFF IFD (Image File Directory) 讀取器。
	/// </summary>
	public class IfdReader : IExifTagReader
	{
		#region 欄位
		readonly BitConverter _BitConverter;
		int _CurrentIfdBufferPosition = -12;
		bool _HasError;
		readonly bool _HasNextIfdOffsetField;
		readonly Ifd _Ifd;
		byte[] _IfdBuffer;
		long _IfdStartPosition;
		long? _NextIfdOffset;
		int _NumberOfTags;
		readonly long _ReferenceStreamPosition;
		readonly Stream _Stream;
		ExifTag _Tag;
		ushort? _TagId;
		int _TagIndex = -1;
		ExifTagType? _TagType;
		#endregion
		#region 建構子
		/// <summary>
		/// 初始化新的 IfdReader 執行個體。
		/// </summary>
		/// <param name="stream">提供 IFD 資料的資料流。</param>
		/// <param name="refStreamPosition">資料流起始參考位置，資料位移的計算將參考此值。</param>
		/// <param name="ifd">IFD。</param>
		/// <param name="bitConverter">提供位元組轉換的 BitConverter。</param>
		/// <param name="hasNextIfdOffsetField">IFD 的尾端是否包含指向下一個 IFD 的欄位。</param>
		/// <exception cref="ArgumentNullException">stream 或 bitConverter 為 Null (Visual Basic 為 Nothing)。</exception>
		/// <exception cref="ArgumentOutOfRangeException">refStreamPosition 為負整數。</exception>
		public IfdReader(Stream stream, long refStreamPosition, Ifd ifd, BitConverter bitConverter, bool hasNextIfdOffsetField = true)
		{
			//確認參數
			if (stream == null)
				throw new ArgumentNullException("stream");
			if (bitConverter == null)
				throw new ArgumentNullException("bitConverter");
			if (refStreamPosition < 0)
				throw new ArgumentOutOfRangeException();

			//初始化
			_Stream = stream;
			_ReferenceStreamPosition = refStreamPosition;
			_BitConverter = bitConverter;
			_Ifd = ifd;
			_HasNextIfdOffsetField = hasNextIfdOffsetField;

			//讀取 IFD
			byte[] buffer = new byte[2];
			try
			{
				_IfdStartPosition = stream.Position;
				if (stream.Read(buffer, 0, 2) == 2)
				{
					_NumberOfTags = bitConverter.ToUInt16(buffer);
					_IfdBuffer = new byte[_NumberOfTags * 12 + (hasNextIfdOffsetField ? 4 : 0)];
					if (stream.Read(_IfdBuffer, 0, _IfdBuffer.Length) == _IfdBuffer.Length)
					{
						if (hasNextIfdOffsetField)
							_NextIfdOffset = bitConverter.ToUInt32(_IfdBuffer, _IfdBuffer.Length - 4);
					}
					else
					{
						_IfdBuffer = null;
						_HasError = true;
					}
				}
				else
					_HasError = true;
			}
			catch
			{
				_IfdBuffer = null;
				_HasError = true;
			}
		}
		#endregion
		#region BitConverter
		/// <summary>
		/// 取得提供位元組轉換的 BitConverter。
		/// </summary>
		public BitConverter BitConverter
		{
			get
			{
				return _BitConverter;
			}
		}
		#endregion
		#region HasError
		/// <summary>
		/// 取得值以確認讀取期間是否發生錯誤。
		/// </summary>
		public bool HasError
		{
			get
			{
				return _HasError;
			}
		}
		#endregion
		#region IExifTagReader.Ifd
		Ifd? IExifTagReader.Ifd
		{
			get
			{
				return _Ifd;
			}
		}
        #endregion
		#region IFD
		/// <summary>
		/// 取得正在讀取的 IFD。
		/// </summary>
		public Ifd Ifd
		{
			get
			{
				return _Ifd;
			}
		}
		#endregion
		#region NumberOfTags
		/// <summary>
		/// 取得 IFD 中所包含的 EXIF Tag 個數。
		/// </summary>
		public int NumberOfTags
		{
			get
			{
				return _NumberOfTags;
			}
		}
		#endregion
		#region NextIfdOffset
		/// <summary>
		/// 取得下一個 IFD 相對於 ReferenceStreamPosition 的位移。
		/// </summary>
		public long? NextIfdOffset
		{
			get
			{
				return _NextIfdOffset;
			}
		}
		#endregion
		#region ReadNextTag
		/// <summary>
		/// 讀取 IFD 中的下一個 EXIF Tag。
		/// </summary>
		/// <returns>True 表示讀取成功，False 表示已讀取至最後一個 EXIF Tag -或- 讀取時發生錯誤。</returns>
		public bool ReadNextTag()
		{
			//確認狀態
			if (_HasError || _TagIndex >= _NumberOfTags || _IfdBuffer == null)
				return false;

			//重設狀態
			_Tag = null;

			//移動讀取位置
			++_TagIndex;
			_CurrentIfdBufferPosition += 12;
			if (_TagIndex >= _NumberOfTags || _CurrentIfdBufferPosition >= _IfdBuffer.Length)
			{
				_TagId = null;
				_TagType = null;
				return false;
			}

			//讀取 Tag ID 及 Type
			_TagId = _BitConverter.ToUInt16(_IfdBuffer, _CurrentIfdBufferPosition);
			_TagType = (ExifTagType)_BitConverter.ToUInt16(_IfdBuffer, _CurrentIfdBufferPosition + 2);

			//完成
			return true;
		}
		#endregion
		#region ReadTag
		bool ReadTag(int tagIndex)
		{
			if (_HasError)
				return false;
			_Tag = null;
			try
			{
				long position = (_IfdStartPosition + 2 + (tagIndex * 12));
				_Stream.Position = position;
				_Tag = new ExifTag(_Stream, _ReferenceStreamPosition, _BitConverter, _Ifd);
				return true;
			}
			catch
			{
				return false;
			}
		}
		#endregion
		#region ReferenceStreamPosition
		/// <summary>
		/// 取得資料流起始參考位置，資料位移的計算將參考此值。
		/// </summary>
		public long ReferenceStreamPosition
		{
			get
			{
				return _ReferenceStreamPosition;
			}
		}
		#endregion
		#region Stream
		/// <summary>
		/// 取得提供 IFD 資料的資料流。
		/// </summary>
		public Stream Stream
		{
			get
			{
				return _Stream;
			}
		}
		#endregion
		#region Tag
		/// <summary>
		/// 取得目前讀取的 EXIF Tag。
		/// </summary>
		public ExifTag Tag
		{
			get
			{
				if (_HasError || _TagIndex < 0 || _TagIndex >= _NumberOfTags)
					return null;
				this.ReadTag(_TagIndex);
				return _Tag;
			}
		}
		#endregion
		#region TagId
		/// <summary>
		/// 取得目前讀取的 EXIF Tag ID。
		/// </summary>
		public ushort? TagId
		{
			get
			{
				return _TagId;
			}
		}
		#endregion
		#region TagType
		/// <summary>
		/// 取得目前讀取的 EXIF Tag 資料類型。
		/// </summary>
		public ExifTagType? TagType
		{
			get
			{
				return _TagType;
			}
		}
		#endregion
	}
}
